﻿/*
 * This file is part of MonoStrategy.
 *
 * Copyright (C) 2010-2011 Christoph Husse
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Authors: 
 *      # Christoph Husse
 * 
 * Also checkout our homepage: http://monostrategy.codeplex.com/
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MonoStrategy;

namespace System
{
    /* C# Version Copyright (C) 2001-2004 Akihilo Kramot (Takel).  */
    /* C# porting from a C-program for MT19937, originaly coded by */
    /* Takuji Nishimura, considering the suggestions by            */
    /* Topher Cooper and Marc Rieffel in July-Aug. 1997.           */
    /* This library is free software under the Artistic license:   */
    /*                                                             */
    /* You can find the original C-program at                      */
    /*     http://www.math.keio.ac.jp/~matumoto/mt.html            */
    /// <summary>
    /// Mersenne Twister Random Generator
    /// </summary>
    [Serializable]
    public class CrossRandom
	{
		/* Period parameters */
		private const int N = 624;
		private const int M = 397;
		private const uint MATRIX_A   = 0x9908b0df; /* constant vector a */
		private const uint UPPER_MASK = 0x80000000; /* most significant w-r bits */
		private const uint LOWER_MASK = 0x7fffffff; /* least significant r bits */

		/* Tempering parameters */
		private const uint TEMPERING_MASK_B = 0x9d2c5680;
		private const uint TEMPERING_MASK_C = 0xefc60000;

        private static uint TEMPERING_SHIFT_U(uint y) { return unchecked(y >> 11); }
        private static uint TEMPERING_SHIFT_S(uint y) { return unchecked(y << 7); }
        private static uint TEMPERING_SHIFT_T(uint y) { return unchecked(y << 15); }
        private static uint TEMPERING_SHIFT_L(uint y) { return unchecked(y >> 18); }

		private uint[] mt = new uint[N]; /* the array for the state vector  */

		private short mti;

		private static uint[] mag01 = { 0x0, MATRIX_A };

		/* initializing the array with a NONZERO seed */
        public CrossRandom(int seed)
        {
            unchecked
            {
                if (seed == 0)
                    seed = 4357;

                /* setting initial seeds to mt[N] using         */
                /* the generator Line 25 of Table 1 in          */
                /* [KNUTH 1981, The Art of Computer Programming */
                /*    Vol. 2 (2nd Ed.), pp102]                  */
                mt[0] = ((uint)seed) & 0xffffffffU;
                for (mti = 1; mti < N; ++mti)
                {
                    mt[mti] = (69069 * mt[mti - 1]) & 0xffffffffU;
                }
            }
        }
        public CrossRandom()
            : this(Environment.TickCount) /* a default initial seed is used   */
		{
		}

        public uint GenerateUInt()
        {
            unchecked
            {
                uint y;

                /* mag01[x] = x * MATRIX_A  for x=0,1 */
                if (mti >= N) /* generate N words at one time */
                {
                    short kk = 0;

                    for (; kk < N - M; ++kk)
                    {
                        y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
                        mt[kk] = mt[kk + M] ^ (y >> 1) ^ mag01[y & 0x1];
                    }

                    for (; kk < N - 1; ++kk)
                    {
                        y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
                        mt[kk] = mt[kk + (M - N)] ^ (y >> 1) ^ mag01[y & 0x1];
                    }

                    y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
                    mt[N - 1] = mt[M - 1] ^ (y >> 1) ^ mag01[y & 0x1];

                    mti = 0;
                }

                y = mt[mti++];
                y ^= TEMPERING_SHIFT_U(y);
                y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B;
                y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C;
                y ^= TEMPERING_SHIFT_L(y);

                return y;
            }
        }

        public virtual uint NextUInt()
		{
			return this.GenerateUInt();
		}

        public virtual uint NextUInt(uint maxValue)
		{
			return (uint)(this.GenerateUInt() / ((double)uint.MaxValue / maxValue));
		}

        public virtual uint NextUInt(uint minValue, uint maxValue) /* throws ArgumentOutOfRangeException */
		{
            unchecked
            {
                if (minValue >= maxValue)
                {
                    throw new ArgumentOutOfRangeException();
                }

                return (uint)(this.GenerateUInt() / ((double)uint.MaxValue / (maxValue - minValue)) + minValue);
            }
		}

        public int Next()
		{
			return this.Next(int.MaxValue);
		}

        public int Next(int maxValue) /* throws ArgumentOutOfRangeException */
		{
            unchecked
            {
                if (maxValue <= 1)
                {
                    if (maxValue < 0)
                    {
                        throw new ArgumentOutOfRangeException();
                    }

                    return 0;
                }

                return (int)(this.NextDouble() * maxValue);
            }
		}

        public int Next(int minValue, int maxValue)
		{
            unchecked
            {
                if (maxValue < minValue)
                {
                    throw new ArgumentOutOfRangeException();
                }
                else if (maxValue == minValue)
                {
                    return minValue;
                }
                else
                {
                    return this.Next(maxValue - minValue) + minValue;
                }
            }
		}

        public void NextBytes(byte[] buffer) /* throws ArgumentNullException*/
		{
            unchecked
            {
                int bufLen = buffer.Length;

                if (buffer == null)
                {
                    throw new ArgumentNullException();
                }

                for (int idx = 0; idx < bufLen; ++idx)
                {
                    buffer[idx] = (byte)this.Next(256);
                }
            }
		}

        public double NextDouble()
		{
            unchecked
            {
                return (double)this.GenerateUInt() / ((ulong)uint.MaxValue + 1);
            }
		}
	}

}
